package com.lxf.clouduser.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.lxf.clouduser.bean.UserBean;
import com.lxf.clouduser.bean.UserServeBean;
import com.lxf.clouduser.mapper.UserItemRepository;
import com.lxf.clouduser.mapper.UserMapper;
import com.lxf.clouduser.mapper.UserServeItemRepository;
import com.lxf.clouduser.mapper.UserServeMapper;
import com.lxf.clouduser.netty.LocalData;
import com.lxf.clouduser.service.UserService;
import com.lxf.clouduser.utils.JwtUtil;
import com.lxf.clouduser.utils.RecommendUtils;
import com.lxf.clouduser.utils.RedisUtils;
import com.lxf.clouduser.utils.SendNettyMsgUtils;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 罗兴发
 * @data 2025/5/19 9:52
 * @description：用户业务实现层
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Resource
    UserMapper userMapper;
    @Resource
    UserServeMapper userServeMapper;
    @Resource
    private JwtUtil jwtUtil;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private UserItemRepository userItemRepository;
    @Resource
    private UserServeItemRepository userServeItemRepository;
    @Resource
    private RecommendUtils recommendUtils;

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Resource
    private SendNettyMsgUtils sendNettyMsgUtils;

    //使用token前缀
    private final String TOKEN_SECRET = "TOKEN_SECRET";
    //48小时
    private final Integer TOKEN_CACHE_TIME = 48 * 60 * 60;

    //推荐数量
    private int RECOMMENDATIONS = 10;

    @Override
    @Transactional(rollbackFor = Exception.class)//开启事务
    public String register(JSONObject json) {
        JSONObject js = new JSONObject();
        //时间
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String addTime = sf.format(new Date());
        //用户名
        String userName = json.getString("user_name");
        //用户密码
        String password = json.getString("password");
        //用户头像
        String userHead = json.getString("user_head");
        if (null==userHead){
            userHead = new String("https://cdn.mediecogroup.com/02/02f8f830/02f8f83013994bdf98b104f9fd5b0c83.jpg");
        }
        //使用huTool的md5双重加密检测
        String s = DigestUtil.md5Hex(password);
        String pwd = DigestUtil.md5Hex(s);
        UserBean login = userMapper.login(userName, pwd);
        if (null==login){
            //用户实例化
            UserBean user = new UserBean();
            user.setUserName(userName);
            user.setUserPwd(pwd);
            user.setUserHead(userHead);
            user.setAddTime(addTime);
            //数组封装
            List<UserBean> list = new ArrayList<>();
            list.add(user);
            //注册
            int flag = userMapper.register(list);
            if (flag<0){
                js.put("code",500);
                js.put("msg","The System Exception!");
            }else {
                js.put("code",200);
                js.put("msg","Success");
                user.setIsOnline(1);
                user.setIsKf(0);
                user.setKfPoints(0);
                //存入es
                userItemRepository.save(user);
            }
        }else {
            js.put("code",402);
            js.put("msg","The User Already Exits!");
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String login(JSONObject json) {
        JSONObject js = new JSONObject();
        //用户名
        String userName = json.getString("user_name");
        //用户密码
        String password = json.getString("password");
        //redis查询
        Object o = redisUtils.get(userName + password);
        if (null!=o){
            //json封装
            js.put("code",200);
            js.put("msg","Success");
            js.put("data",o);
        }else {
            //使用huTool的md5双重加密检测
            String s = DigestUtil.md5Hex(password);
            String pwd = DigestUtil.md5Hex(s);

            UserBean user = userMapper.login(userName,pwd);
            if (null!=user){
                Integer userId = user.getUserId();
                //使用用户名和密码创建token
                String token = jwtUtil.createToken(userId, userName);
                //使用token秘钥加上token，一同redis缓存
                String secretToken = TOKEN_SECRET+token+":"+userId;
                boolean set = redisUtils.set(secretToken, secretToken, TOKEN_CACHE_TIME);
                if (!set){
                    js.put("code",500);
                    js.put("msg","The Redis Cache Exception !");
                }else {
                    //更改在线状态
                    userMapper.updateUserStatusByUserId(userId,1);

                    user.setIsOnline(1);
                    userItemRepository.save(user);

                    //时间
                    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String loginTime = sf.format(new Date());

                    //存储用户信息，
                    Map<String,Object> map = new HashMap<>();
                    map.put("userId",userId);
                    map.put("userName",userName);
                    map.put("loginTime",loginTime);
                    map.put("token",secretToken);

                    //json封装
                    js.put("code",200);
                    js.put("msg","Success");
                    js.put("data",map);
                }
            }else {
                js.put("code",401);
                js.put("msg","The UserName Or Password Is Incorrect");
            }
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    /**
     * 修改密码接口
     * @param json
     * @param request
     * @return
     */
    @Override
    public String updatePwd(JSONObject json,HttpServletRequest request) {
        JSONObject js = new JSONObject();
        //获取请求凭证,凭证中存放用户ID和用户名，存放格式为：userId_userName
        String secretToken = request.getHeader("secretToken");

        // 新密码
        String newPassword = json.getString("new_password");

        // 旧密码
        String oldPassword = json.getString("old_password");

        // 使用huTool的md5双重加密检测
        String s = DigestUtil.md5Hex(newPassword);
        String pwd = DigestUtil.md5Hex(s);

        //获取redis中缓存的token，
        // 用以判断该用户是在忘记密码情况下调用还是已知密码情况下调用
        Object o = redisUtils.get(secretToken);
        if (null!=o){
            //截取请求头中存储的用户ID
            int secretTokenIndex = secretToken.lastIndexOf(":");
            String userIdSub = secretToken.substring(secretTokenIndex+1);
            Integer userId = Integer.valueOf(userIdSub);//用户ID

            // 根据用户ID查询旧密码
            Optional<UserBean> users = userItemRepository.findById(userIdSub);
            if (users.isPresent()){
                UserBean userBean = users.get();
                String userPwd = userBean.getUserPwd();

                // 使用huTool的md5对旧密码进行双重加密检测
                String oldStr = DigestUtil.md5Hex(oldPassword);
                String oldPwd = DigestUtil.md5Hex(oldStr);

                // 旧密码输入不正确的情况
                if (!userPwd.equals(oldPwd)){
                    js.put("code",401);
                    js.put("msg","The Old Password Is Error!");
                }else {
                    // 新密码与旧密码是否一致的情况判断
                    if (newPassword.equals(oldPassword)){
                        // 若一致，则需要提示，不能与旧密码一致
                        js.put("code",401);
                        js.put("msg","The New Password Cannot Be The Same As The Old One!");
                    }else {
                        // 更新密码操作
                        int flag = userMapper.updatePwdByUserId(userId,pwd);
                        if (flag<0){
                            js.put("code",401);
                            js.put("msg","Fails");
                        }else {
                            js.put("code",200);
                            js.put("msg","Success");

                            // 删除token，需要重新登录
                            redisUtils.del(secretToken);

                            // 更新es中的信息
                            userBean.setUserPwd(pwd);
                            userItemRepository.save(userBean);
                        }
                    }
                }
            }else {
                js.put("code",500);
                js.put("msg","Exception, Please Try Again Later");
            }
        }else {
//            // 需要手机号接受验证码（先省略）
//            // 根据手机号查询用户信息
//            // 更新密码操作
//            int flag = userMapper.updatePwdByUserId(userId,pwd);
//            if (flag<0){
//                js.put("code",401);
//                js.put("msg","Fails");
//            }else {
//                js.put("code",200);
//                js.put("msg","Success");
//
//                // 更新es中的信息
//                userBean.setUserPwd(pwd);
//                userItemRepository.save(userBean);
//            }
//            // 根据用户ID查询旧密码
//            Optional<UserBean> users = userItemRepository.findById(userIdSub);
//            if (users.isPresent()){
//                UserBean userBean = users.get();
//            }
        }

        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    /**
     * 注销账号
     * @param request
     * @return
     */
    @Override
    public String logout(HttpServletRequest request) {
        JSONObject js = new JSONObject();

        //获取请求凭证,凭证中存放用户ID和用户名，存放格式为：userId_userName
        String secretToken = request.getHeader("secretToken");

        //获取redis中缓存的token，
        // 用以判断该用户是在忘记密码情况下调用还是已知密码情况下调用
        Object o = redisUtils.get(secretToken);
        if (null!=o){
            //截取请求头中存储的用户ID
            int secretTokenIndex = secretToken.lastIndexOf(":");
            String userIdSub = secretToken.substring(secretTokenIndex+1);
            Integer userId = Integer.valueOf(userIdSub);//用户ID

            // 根据用户ID删除用户信息
            int flag = userMapper.delUserById(userId);
            if (flag<0){
                js.put("code",400);
                js.put("msg","Fails");
            }else {
                js.put("code",200);
                js.put("msg","Success");

                // 删除token,防止用户使用该token胡乱操作
                redisUtils.del(secretToken);

                // 删除es中用户信息
                userItemRepository.deleteById(userIdSub);
            }
        }else {
            js.put("code",400);
            js.put("msg","The Current Request Is Invalid");
        }
        return JSON.toJSONString(js,SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String getUser(JSONObject json) {
        JSONObject js = new JSONObject();
        // 前端参数
        String userName = json.getString("user_name");
        Integer page = json.getInteger("page");
        Integer size = json.getInteger("size");

        // 计算初始页
        Integer startPage = (page-1)*size;

        // 1. 构建查询条件
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery(userName, "userName"))
                .withPageable(PageRequest.of(startPage, size));

        // 2. 添加高亮配置
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("userName")
                .preTags("<span style='color:red'>")
                .postTags("</span>");
        queryBuilder.withHighlightBuilder(highlightBuilder);

        // 3. 执行查询
        SearchHits<UserBean> hits = elasticsearchRestTemplate
                .search(queryBuilder.build(), UserBean.class);

        // 4. 处理高亮结果
        List<UserBean> list = hits.stream()
                .map(hit -> {
                    UserBean doc = hit.getContent();
                    Map<String, List<String>> highlightFields = hit.getHighlightFields();
                    highlightFields.forEach((field, texts) -> {
                        try {
                            Field declaredField = doc.getClass().getDeclaredField(field);
                            declaredField.setAccessible(true);
                            declaredField.set(doc, texts.get(0));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    });
                    return doc;
                }).collect(Collectors.toList());
        // 7. 构造分页结果
        PageImpl<UserBean> userBeans = new PageImpl<>(list, PageRequest.of(startPage, size), hits.getTotalHits());

        js.put("data",userBeans);
        js.put("code",200);
        js.put("msg","Success");

        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String getUserByUserId(JSONObject json) {
        JSONObject js = new JSONObject();
        //参数
        String userId = json.getString("user_id");
        //根据用户ID获取用户信息
        UserBean userBean = userMapper.selectByUserId(userId);

        //json封装
        js.put("code",200);
        js.put("msg","Success");
        js.put("data",userBean);
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    /**
     * 此接口只允许超级管理员调用(设置客服)
     * @param json
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String setUserKfByUserId(JSONObject json, HttpServletRequest request) {
        JSONObject js = new JSONObject();

        //获取请求凭证,凭证中存放用户ID和用户名，存放格式为：userId_userName
        String secretToken = request.getHeader("secretToken");

        //获取redis中缓存的token
        Object o = redisUtils.get(secretToken);
        if (null!=o){
            //截取请求头中存储的用户ID
            int secretTokenIndex = secretToken.lastIndexOf(":");
            String userIdSub = secretToken.substring(secretTokenIndex+1);
            Integer userId = Integer.valueOf(userIdSub);//用户ID
            //商品编码
            String uid = json.getString("user_id");
            Integer isKf = json.getInteger("is_kf");
            Integer kfCode = json.getInteger("kf_code");
            //查询es中的信息
            Optional<UserBean> u = userItemRepository.findById(uid);
            if (u.isPresent()){
                UserBean user = u.get();

                //将用户设置为客服
                user.setIsKf(isKf);
                user.setKfCode(kfCode);
                int flag = userMapper.updateUserKfByUserId(Integer.valueOf(uid),isKf,kfCode);
                if (flag<0){
                    log.info("更新失败");
                    js.put("code",401);
                    js.put("msg","fails");
                }else {
                    log.info("更新成功");
                    js.put("code",200);
                    js.put("msg","Success");

                    try {
                        //消息内容
                        String msg = "管理员已将你设置为客服，请注意查收";

                        //给该位新客服发送消息通知
                        sendNettyMsgUtils.pushMsgToUser(2,"1",uid,msg);
                    }catch (Exception e){
                        e.printStackTrace();
                    }

                    //更新es中的数据
                    userItemRepository.save(user);
                }
            }else {
                js.put("code",402);
                js.put("msg","The User Already Log Off !");
            }
        }else {
            js.put("code",400);
            js.put("msg","The Current Request is Invalid");
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String findById(JSONObject json) {
        JSONObject js = new JSONObject();

        String userId = json.getString("user_id");
        Optional<UserBean> u = userItemRepository.findById(userId);
        if (u.isPresent()){
            UserBean userBean = u.get();
            js.put("data",userBean);
        }else {
            js.put("data",null);
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String deleteUserEsData() {
        JSONObject js = new JSONObject();

        //删除es
        userItemRepository.deleteAll();

        js.put("code",200);
        js.put("msg","Success");
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    public String recommend(JSONObject json, HttpServletRequest request) {
        JSONObject js = new JSONObject();

        //获取请求凭证,凭证中存放用户ID和用户名，存放格式为：userId_userName
        String secretToken = request.getHeader("secretToken");

        //获取redis中缓存的token
        Object o = redisUtils.get(secretToken);
        if (null!=o){
            //截取请求头中存储的用户ID
            int secretTokenIndex = secretToken.lastIndexOf(":");
            String userIdSub = secretToken.substring(secretTokenIndex+1);
            Integer userId = Integer.valueOf(userIdSub);//用户ID

            //需要推荐的类别
            Integer type = json.getInteger("type");

            //装载查询数据的ID集合
            List<String>  l = new ArrayList<>();

            //推荐数量
            Integer recommendations = RECOMMENDATIONS;
            //获取最终推荐数据
            List<String> list = recommendUtils.recommend(userId, recommendations,type);
            for (int i=0; i<list.size(); i++){
                String s = list.get(i);
                StringBuilder sb = new StringBuilder(s);
                sb.deleteCharAt(s.length() - 1);
                String result = sb.toString();

                //list添加
                l.add(result);
            }

            log.info("推荐的用户ID：{}",l);

            if (l.size()>0){
                //批量查询
                Iterable<UserBean> userList = userItemRepository.findAllById(l);
                js.put("data",userList);
            }else {
                List<UserBean> user = userMapper.getRandUser(userIdSub);
                js.put("data",user);
            }

            log.info("用户推荐成功");
            js.put("code",200);
            js.put("msg","Success");
        }else {
            js.put("code",400);
            js.put("msg","The Current Request is Invalid");
        }
        return JSONObject.toJSONString(js, SerializerFeature.WriteMapNullValue);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void setUserStatus() {

        // 装载需要将人工请求的状态设置为取消状态的用户ID集合
        List<Integer> idList = new ArrayList<>();

        // 存储当前在线的用户ID集合
        List<Integer> userIdList = new ArrayList<>();

        // 获取在线用户列表
        Map<String, Channel> map = LocalData.channelUserUrl;
        if (map.size()>0){

            // 使用Java 8+的Stream API遍历Map，每次循环取出键值对的值
            map.entrySet().stream().forEach(entry -> {
                String userId = entry.getKey();
                Integer uId = Integer.valueOf(userId);
                // 将用户ID存入集合中
                userIdList.add(uId);
            });

            //将未在线用户设置为离线状态
            int i = userMapper.setUserStatus(userIdList,0);
            if (i>=0){

                //将在线的用户更改为在线状态
                int i0 = userMapper.setUserStatus(userIdList, 1);
                //人工客服请求状态
                Integer status = 0;

                //查询不在线用户信息
                List<UserBean> list = userMapper.getUserByUserIds(userIdList);
                for (UserBean user: list) {
                    Integer userId = user.getUserId();
                    Integer isKf = user.getIsKf();
                    if (isKf==0){
                        //根据用户ID将用户人工客服请求的数据更新为取消状态
                        List<UserServeBean> l = userServeMapper.getUserServeById(userId,status);
                        for (UserServeBean userServe:l) {
                            if (null!=userServe){
                                userServe.setIsServe(3);

                                // 将服务状态更改为取消状态
                                userServeItemRepository.save(userServe);
                            }
                        }

                        //装入list集合中
                        idList.add(userId);
                    }

                    // 将不在线用户设置为离线状态
                    user.setIsOnline(0);
                    userItemRepository.save(user);

                    //关闭不在线的ws通道，释放相应资源
                    Channel channel = map.get(userId);
                    if (null!=channel)channel.close();
                }
            }

            //批量将请求人工服务数据的状态设置为取消状态
            int i1 = userServeMapper.updateUserServeStatus(idList);
        }
    }
}
